/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreStableHeaders.h"

#include "OgreParticleSystemEx.h"
#include "OgreParticleSystemManagerEx.h"
#include "OgreRenderQueue.h"
#include "OgreBillboardSet.h"
#include "OgreParticleEmitterEx.h"
#include "OgreParticleAffectorEx.h"
#include "OgreParticleEx.h"
#include "OgreSceneNode.h"
#include "OgreCamera.h"
#include "OgreStringConverter.h"
#include "OgreLogManager.h"
#include "OgreException.h"
#include "OgreParticleAffectorFactoryEx.h"
#include "OgreParticleSystemRendererEx.h"
#include "OgreMaterialManager.h"
#include "OgreSceneManager.h"
#include "OgreControllerManager.h"
#include "OgreRoot.h"

namespace Ogre {
    // Init statics
    ParticleSystemEx::CmdCull ParticleSystemEx::msCullCmd;
    ParticleSystemEx::CmdHeight ParticleSystemEx::msHeightCmd;
    ParticleSystemEx::CmdMaterial ParticleSystemEx::msMaterialCmd;
    ParticleSystemEx::CmdQuota ParticleSystemEx::msQuotaCmd;
	ParticleSystemEx::CmdEmittedEmitterQuota ParticleSystemEx::msEmittedEmitterQuotaCmd;
    ParticleSystemEx::CmdWidth ParticleSystemEx::msWidthCmd;
    ParticleSystemEx::CmdRenderer ParticleSystemEx::msRendererCmd;
	ParticleSystemEx::CmdSorted ParticleSystemEx::msSortedCmd;
	ParticleSystemEx::CmdLocalSpace ParticleSystemEx::msLocalSpaceCmd;
	ParticleSystemEx::CmdIterationInterval ParticleSystemEx::msIterationIntervalCmd;
	ParticleSystemEx::CmdNonvisibleTimeout ParticleSystemEx::msNonvisibleTimeoutCmd;

    RadixSort<ParticleSystemEx::ActiveParticleList, ParticleEx*, float> ParticleSystemEx::mRadixSorter;

    Real ParticleSystemEx::msDefaultIterationInterval = 0;
    Real ParticleSystemEx::msDefaultNonvisibleTimeout = 0;

	//-----------------------------------------------------------------------
	// Local class for updating based on time
	class ParticleSystemUpdateValue : public ControllerValue<Real>
	{
	protected:
		ParticleSystemEx* mTarget;
	public:
		ParticleSystemUpdateValue(ParticleSystemEx* target) : mTarget(target) {}

		Real getValue(void) const { return 0; } // N/A

		void setValue(Real value) { mTarget->_update(value); }

	};
    //-----------------------------------------------------------------------
    ParticleSystemEx::ParticleSystemEx() 
      : mAABB(),
        mBoundingRadius(1.0f),
        mBoundsAutoUpdate(true),
        mBoundsUpdateTime(10.0f),
        mUpdateRemainTime(0),
        mWorldAABB(),
        mResourceGroupName(ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME),
        mIsRendererConfigured(false),
        mSpeedFactor(1.0f),
        mIterationInterval(0),
		mIterationIntervalSet(false),
        mSorted(false),
        mLocalSpace(false),
		mNonvisibleTimeout(0),
		mNonvisibleTimeoutSet(false),
		mTimeSinceLastVisible(0),
		mLastVisibleFrame(0),
        mTimeController(0),
		mEmittedEmitterPoolInitialised(false),
		mIsEmitting(true),
        mRenderer(0),
        mCullIndividual(false),
        mPoolSize(0),
        mEmittedEmitterPoolSize(0)
	{
        initParameters();

        // Default to billboard renderer
        setRenderer("billboard");

    }
    //-----------------------------------------------------------------------
    ParticleSystemEx::ParticleSystemEx(const String& name, const String& resourceGroup)
      : MovableObject(name),
        mAABB(),
        mBoundingRadius(1.0f),
        mBoundsAutoUpdate(true),
        mBoundsUpdateTime(10.0f),
        mUpdateRemainTime(0),
        mWorldAABB(),
        mResourceGroupName(resourceGroup),
        mIsRendererConfigured(false),
        mSpeedFactor(1.0f),
        mIterationInterval(0),
		mIterationIntervalSet(false),
        mSorted(false),
        mLocalSpace(false),
		mNonvisibleTimeout(0),
		mNonvisibleTimeoutSet(false),
		mTimeSinceLastVisible(0),
		mLastVisibleFrame(Root::getSingleton().getNextFrameNumber()),
        mTimeController(0),
		mEmittedEmitterPoolInitialised(false),
		mIsEmitting(true),
        mRenderer(0), 
        mCullIndividual(false),
        mPoolSize(0),
        mEmittedEmitterPoolSize(0)
    {
        setDefaultDimensions( 100, 100 );
        setMaterialName( "BaseWhite" );
        // Default to 10 particles, expect app to specify (will only be increased, not decreased)
        setParticleQuota( 10 );
		setEmittedEmitterQuota( 3 );
        initParameters();

        // Default to billboard renderer
        setRenderer("billboard");
    }
    //-----------------------------------------------------------------------
    ParticleSystemEx::~ParticleSystemEx()
    {
        if (mTimeController)
        {
            // Destroy controller
            ControllerManager::getSingleton().destroyController(mTimeController);
            mTimeController = 0;
        }

		// Arrange for the deletion of emitters & affectors
        removeAllEmitters();
		removeAllEmittedEmitters();
        removeAllAffectors();

		// Deallocate all particles
		destroyVisualParticles(0, mParticlePool.size());
        // Free pool items
        ParticlePool::iterator i;
        for (i = mParticlePool.begin(); i != mParticlePool.end(); ++i)
        {
            OGRE_DELETE *i;
        }

        if (mRenderer)
        {
            ParticleSystemManagerEx::getSingleton()._destroyRenderer(mRenderer);
            mRenderer = 0;
        }

    }
    //-----------------------------------------------------------------------
    ParticleEmitterEx* ParticleSystemEx::addEmitter(const String& emitterType)
    {
        ParticleEmitterEx* em = 
            ParticleSystemManagerEx::getSingleton()._createEmitter(emitterType, this);
        mEmitters.push_back(em);
        return em;
    }
    //-----------------------------------------------------------------------
    ParticleEmitterEx* ParticleSystemEx::getEmitter(unsigned short index) const
    {
        assert(index < mEmitters.size() && "Emitter index out of bounds!");
        return mEmitters[index];
    }
    //-----------------------------------------------------------------------
    unsigned short ParticleSystemEx::getNumEmitters(void) const
    {
        return static_cast< unsigned short >( mEmitters.size() );
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::removeEmitter(unsigned short index)
    {
        assert(index < mEmitters.size() && "Emitter index out of bounds!");
        ParticleEmitterList::iterator ei = mEmitters.begin() + index;
        ParticleSystemManagerEx::getSingleton()._destroyEmitter(*ei);
        mEmitters.erase(ei);
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::removeAllEmitters(void)
    {
        // DON'T delete directly, we don't know what heap these have been created on
        ParticleEmitterList::iterator ei;
        for (ei = mEmitters.begin(); ei != mEmitters.end(); ++ei)
        {
            ParticleSystemManagerEx::getSingleton()._destroyEmitter(*ei);
        }
        mEmitters.clear();
    }
    //-----------------------------------------------------------------------
    ParticleAffectorEx* ParticleSystemEx::addAffector(const String& affectorType)
    {
        ParticleAffectorEx* af = 
            ParticleSystemManagerEx::getSingleton()._createAffector(affectorType, this);
        mAffectors.push_back(af);
        return af;
    }
    //-----------------------------------------------------------------------
    ParticleAffectorEx* ParticleSystemEx::getAffector(unsigned short index) const
    {
        assert(index < mAffectors.size() && "Affector index out of bounds!");
        return mAffectors[index];
    }
    //-----------------------------------------------------------------------
    unsigned short ParticleSystemEx::getNumAffectors(void) const
    {
        return static_cast< unsigned short >( mAffectors.size() );
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::removeAffector(unsigned short index)
    {
        assert(index < mAffectors.size() && "Affector index out of bounds!");
        ParticleAffectorList::iterator ai = mAffectors.begin() + index;
        ParticleSystemManagerEx::getSingleton()._destroyAffector(*ai);
        mAffectors.erase(ai);
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::removeAllAffectors(void)
    {
        // DON'T delete directly, we don't know what heap these have been created on
        ParticleAffectorList::iterator ai;
        for (ai = mAffectors.begin(); ai != mAffectors.end(); ++ai)
        {
            ParticleSystemManagerEx::getSingleton()._destroyAffector(*ai);
        }
        mAffectors.clear();
    }
    //-----------------------------------------------------------------------
    ParticleSystemEx& ParticleSystemEx::operator=(const ParticleSystemEx& rhs)
    {
        // Blank this system's emitters & affectors
        removeAllEmitters();
		removeAllEmittedEmitters();
        removeAllAffectors();

        // Copy emitters
        for(unsigned short i = 0; i < rhs.getNumEmitters(); ++i)
        {
            ParticleEmitterEx* rhsEm = rhs.getEmitter(i);
            ParticleEmitterEx* newEm = addEmitter(rhsEm->getType());
            rhsEm->copyParametersTo(newEm);
        }
        // Copy affectors
        for(unsigned short i = 0; i < rhs.getNumAffectors(); ++i)
        {
            ParticleAffectorEx* rhsAf = rhs.getAffector(i);
            ParticleAffectorEx* newAf = addAffector(rhsAf->getType());
            rhsAf->copyParametersTo(newAf);
        }
        setParticleQuota(rhs.getParticleQuota());
		setEmittedEmitterQuota(rhs.getEmittedEmitterQuota());
        setMaterialName(rhs.mMaterialName);
        setDefaultDimensions(rhs.mDefaultWidth, rhs.mDefaultHeight);
        mCullIndividual = rhs.mCullIndividual;
		mSorted = rhs.mSorted;
		mLocalSpace = rhs.mLocalSpace;
		mIterationInterval = rhs.mIterationInterval;
		mIterationIntervalSet = rhs.mIterationIntervalSet;
		mNonvisibleTimeout = rhs.mNonvisibleTimeout;
		mNonvisibleTimeoutSet = rhs.mNonvisibleTimeoutSet;
		// last frame visible and time since last visible should be left default

        setRenderer(rhs.getRendererName());
        // Copy settings
        if (mRenderer && rhs.getRenderer())
        {
            rhs.getRenderer()->copyParametersTo(mRenderer);
        }

        return *this;

    }
    //-----------------------------------------------------------------------
    size_t ParticleSystemEx::getNumParticles(void) const
    {
        return mActiveParticles.size();
    }
    //-----------------------------------------------------------------------
    size_t ParticleSystemEx::getParticleQuota(void) const
    {
        return mPoolSize;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setParticleQuota(size_t size)
    {
        // Never shrink below size()
        size_t currSize = mParticlePool.size();

        if( currSize < size )
        {
            // Will allocate particles on demand
            mPoolSize = size;
            
        }
    }
	//-----------------------------------------------------------------------
	size_t ParticleSystemEx::getEmittedEmitterQuota(void) const
	{
		return mEmittedEmitterPoolSize;
	}
	//-----------------------------------------------------------------------
	void ParticleSystemEx::setEmittedEmitterQuota(size_t size)
	{
		// Never shrink below size()
		EmittedEmitterPool::iterator i;
		size_t currSize = 0;
		for (i = mEmittedEmitterPool.begin(); i != mEmittedEmitterPool.end(); ++i)
		{
			currSize += i->second.size();
		}

		if( currSize < size )
		{
			// Will allocate emitted emitters on demand
			mEmittedEmitterPoolSize = size;
		}
	}
    //-----------------------------------------------------------------------
	void ParticleSystemEx::setNonVisibleUpdateTimeout(Real timeout)
	{
		mNonvisibleTimeout = timeout;
		mNonvisibleTimeoutSet = true;
	}
    //-----------------------------------------------------------------------
	void ParticleSystemEx::setIterationInterval(Real interval)
	{
		mIterationInterval = interval;
		mIterationIntervalSet = true;
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_update(Real timeElapsed)
    {
        // Only update if attached to a node
        if (!mParentNode)
            return;

		Real nonvisibleTimeout = mNonvisibleTimeoutSet ?
			mNonvisibleTimeout : msDefaultNonvisibleTimeout;

		if (nonvisibleTimeout > 0)
		{
			// Check whether it's been more than one frame (update is ahead of
			// camera notification by one frame because of the ordering)
			long frameDiff = Root::getSingleton().getNextFrameNumber() - mLastVisibleFrame;
			if (frameDiff > 1 || frameDiff < 0) // < 0 if wrap only
			{
				mTimeSinceLastVisible += timeElapsed;
				if (mTimeSinceLastVisible >= nonvisibleTimeout)
				{
					// No update
					return;
				}
			}
		}

		// Scale incoming speed for the rest of the calculation
		timeElapsed *= mSpeedFactor;

        // Init renderer if not done already
        configureRenderer();

		// Initialise emitted emitters list if not done already
		initialiseEmittedEmitters();

		Real iterationInterval = mIterationIntervalSet ? 
            mIterationInterval : msDefaultIterationInterval;
        if (iterationInterval > 0)
        {
            mUpdateRemainTime += timeElapsed;

            while (mUpdateRemainTime >= iterationInterval)
            {
                // Update existing particles
                _expire(iterationInterval);
                _triggerAffectors(iterationInterval);
                _applyMotion(iterationInterval);

				if(mIsEmitting)
				{
					// Emit new particles
					_triggerEmitters(iterationInterval);
				}

                mUpdateRemainTime -= iterationInterval;
            }
        }
        else
        {
            // Update existing particles
            _expire(timeElapsed);
            _triggerAffectors(timeElapsed);
            _applyMotion(timeElapsed);

			if(mIsEmitting)
			{
				// Emit new particles
				_triggerEmitters(timeElapsed);
			}
        }

        if (!mBoundsAutoUpdate && mBoundsUpdateTime > 0.0f)
            mBoundsUpdateTime -= timeElapsed; // count down 
        _updateBounds();

    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_expire(Real timeElapsed)
    {
        ActiveParticleList::iterator i, itEnd;
        ParticleEx* pParticle;
		ParticleEmitterEx* pParticleEmitter;

        itEnd = mActiveParticles.end();

        for (i = mActiveParticles.begin(); i != itEnd; )
        {
            pParticle = static_cast<ParticleEx*>(*i);
            if (pParticle->timeToLive < timeElapsed)
            {
                // Notify renderer
                mRenderer->_notifyParticleExpired(pParticle);

				// Identify the particle type
				if (pParticle->particleType == ParticleEx::Visual)
				{
	                // Destroy this one
		            mFreeParticles.splice(mFreeParticles.end(), mActiveParticles, i++);
				}
				else
				{
					// For now, it can only be an emitted emitter
					pParticleEmitter = static_cast<ParticleEmitterEx*>(*i);
					list<ParticleEmitterEx*>::type* fee = findFreeEmittedEmitter(pParticleEmitter->getName());
					fee->push_back(pParticleEmitter);

					// Also erase from mActiveEmittedEmitters
					removeFromActiveEmittedEmitters (pParticleEmitter);

					// And erase from mActiveParticles
					i = mActiveParticles.erase( i );
				}
            }
            else
            {
                // Decrement TTL
                pParticle->timeToLive -= timeElapsed;
				++i;
            }

        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_triggerEmitters(Real timeElapsed)
    {
        // Add up requests for emission
        static vector<unsigned>::type requested;
        if( requested.size() != mEmitters.size() )
            requested.resize( mEmitters.size() );

        size_t totalRequested, emitterCount, i, emissionAllowed;
        ParticleEmitterList::iterator itEmit, iEmitEnd;
		ActiveEmittedEmitterList::iterator itActiveEmit;
        iEmitEnd = mEmitters.end();
        emitterCount = mEmitters.size();
        emissionAllowed = mFreeParticles.size();
        totalRequested = 0;

        // Count up total requested emissions for regular emitters (and exclude the ones that are used as
		// a template for emitted emitters)
        for (itEmit = mEmitters.begin(), i = 0; itEmit != iEmitEnd; ++itEmit, ++i)
        {
			if (!(*itEmit)->isEmitted())
			{
				requested[i] = (*itEmit)->_getEmissionCount(timeElapsed);
				totalRequested += requested[i];
			}
        }

		// Add up total requested emissions for (active) emitted emitters
		for (itActiveEmit = mActiveEmittedEmitters.begin(); itActiveEmit != mActiveEmittedEmitters.end(); ++itActiveEmit)
		{
			totalRequested += (*itActiveEmit)->_getEmissionCount(timeElapsed);
		}

        // Check if the quota will be exceeded, if so reduce demand
		Real ratio =  1.0f;
        if (totalRequested > emissionAllowed)
        {
            // Apportion down requested values to allotted values
            ratio =  (Real)emissionAllowed / (Real)totalRequested;
            for (i = 0; i < emitterCount; ++i)
            {
                requested[i] = static_cast<unsigned>(requested[i] * ratio);
            }
        }

        // Emit
		// For each emission, apply a subset of the motion for the frame
		// this ensures an even distribution of particles when many are
		// emitted in a single frame
        for (itEmit = mEmitters.begin(), i = 0; itEmit != iEmitEnd; ++itEmit, ++i)
        {
			// Trigger the emitters, but exclude the emitters that are already in the emitted emitters list; 
			// they are handled in a separate loop
			if (!(*itEmit)->isEmitted())
				_executeTriggerEmitters (*itEmit, static_cast<unsigned>(requested[i]), timeElapsed);
        }

		// Do the same with all active emitted emitters
		for (itActiveEmit = mActiveEmittedEmitters.begin(), i = 0; itActiveEmit != mActiveEmittedEmitters.end(); ++itActiveEmit, ++i)
			_executeTriggerEmitters (*itActiveEmit, static_cast<unsigned>((*itActiveEmit)->_getEmissionCount(timeElapsed) * ratio), timeElapsed);
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_executeTriggerEmitters(ParticleEmitterEx* emitter, unsigned requested, Real timeElapsed)
    {
		ParticleAffectorList::iterator	itAff, itAffEnd;
		Real timePoint = 0.0f;


        // avoid any divide by zero conditions
		if(!requested) 
			return;

		Real timeInc = timeElapsed / requested;

		for (unsigned int j = 0; j < requested; ++j)
		{
			// Create a new particle & init using emitter
			// The particle is a visual particle if the emit_emitter property of the emitter isn't set 
			ParticleEx* p = 0;
			String	emitterName = emitter->getEmittedEmitter();
			if (emitterName == StringUtil::BLANK)
				p = createParticle();
			else
				p = createEmitterParticle(emitterName);

			// Only continue if the particle was really created (not null)
			if (!p)
				return;

			emitter->_initParticle(p);

			// Translate position & direction into world space
			if (!mLocalSpace)
			{
				p->position  = 
					(mParentNode->_getDerivedOrientation() *
					(mParentNode->_getDerivedScale() * p->position))
					+ mParentNode->_getDerivedPosition();
				p->direction = 
					(mParentNode->_getDerivedOrientation() * p->direction);
			}

			// apply partial frame motion to this particle
            p->position += (p->direction * timePoint);

			// apply particle initialization by the affectors
			itAffEnd = mAffectors.end();
			for (itAff = mAffectors.begin(); itAff != itAffEnd; ++itAff)
				(*itAff)->_initParticle(p);

			// Increment time fragment
			timePoint += timeInc;

			if (p->particleType == ParticleEx::Emitter)
			{
				// If the particle is an emitter, the position on the emitter side must also be initialised
				// Note, that position of the emitter becomes a position in worldspace if mLocalSpace is set 
				// to false (will this become a problem?)
				ParticleEmitterEx* pParticleEmitter = static_cast<ParticleEmitterEx*>(p);
				pParticleEmitter->setPosition(p->position);
			}

            // Notify renderer
            mRenderer->_notifyParticleEmitted(p);
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_applyMotion(Real timeElapsed)
    {
        ActiveParticleList::iterator i, itEnd;
        ParticleEx* pParticle;
		ParticleEmitterEx* pParticleEmitter;

        itEnd = mActiveParticles.end();
        for (i = mActiveParticles.begin(); i != itEnd; ++i)
        {
            pParticle = static_cast<ParticleEx*>(*i);
            pParticle->position += (pParticle->direction * timeElapsed);

			if (pParticle->particleType == ParticleEx::Emitter)
			{
				// If it is an emitter, the emitter position must also be updated
				// Note, that position of the emitter becomes a position in worldspace if mLocalSpace is set 
				// to false (will this become a problem?)
				pParticleEmitter = static_cast<ParticleEmitterEx*>(*i);
				pParticleEmitter->setPosition(pParticle->position);
			}
        }

        // Notify renderer
        mRenderer->_notifyParticleMoved(mActiveParticles);
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_triggerAffectors(Real timeElapsed)
    {
        ParticleAffectorList::iterator i, itEnd;
        
        itEnd = mAffectors.end();
        for (i = mAffectors.begin(); i != itEnd; ++i)
        {
            (*i)->_affectParticles(this, timeElapsed);
        }

    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::increasePool(size_t size)
    {
        size_t oldSize = mParticlePool.size();

        // Increase size
        mParticlePool.reserve(size);
        mParticlePool.resize(size);

        // Create new particles
        for( size_t i = oldSize; i < size; i++ )
		{
            mParticlePool[i] = OGRE_NEW ParticleEx();
		}

		if (mIsRendererConfigured)
		{
			createVisualParticles(oldSize, size);
		}


    }
    //-----------------------------------------------------------------------
    ParticleIteratorEx ParticleSystemEx::_getIterator(void)
    {
        return ParticleIteratorEx(mActiveParticles.begin(), mActiveParticles.end());
    }
    //-----------------------------------------------------------------------
	ParticleEx* ParticleSystemEx::getParticle(size_t index) 
	{
		assert (index < mActiveParticles.size() && "Index out of bounds!");
		ActiveParticleList::iterator i = mActiveParticles.begin();
		std::advance(i, index);
		return *i;
	}
    //-----------------------------------------------------------------------
    ParticleEx* ParticleSystemEx::createParticle(void)
    {
		ParticleEx* p = 0;
		if (!mFreeParticles.empty())
		{
	        // Fast creation (don't use superclass since emitter will init)
	        p = mFreeParticles.front();
	        mActiveParticles.splice(mActiveParticles.end(), mFreeParticles, mFreeParticles.begin());

			p->_notifyOwner(this);
		}

        return p;

    }
    //-----------------------------------------------------------------------
    ParticleEx* ParticleSystemEx::createEmitterParticle(const String& emitterName)
    {
		// Get the appropriate list and retrieve an emitter	
		ParticleEx* p = 0;
		list<ParticleEmitterEx*>::type* fee = findFreeEmittedEmitter(emitterName);
		if (fee && !fee->empty())
		{
	        p = fee->front();
			p->particleType = ParticleEx::Emitter;
			fee->pop_front();
			mActiveParticles.push_back(p);

			// Also add to mActiveEmittedEmitters. This is needed to traverse through all active emitters
			// that are emitted. Don't use mActiveParticles for that (although they are added to
			// mActiveParticles also), because it would take too long to traverse.
			mActiveEmittedEmitters.push_back(static_cast<ParticleEmitterEx*>(p));
			
			p->_notifyOwner(this);
		}

        return p;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_updateRenderQueue(RenderQueue* queue)
    {
        if (mRenderer)
        {
            mRenderer->_updateRenderQueue(queue, mActiveParticles, mCullIndividual);
        }
    }
	//---------------------------------------------------------------------
	void ParticleSystemEx::visitRenderables(Renderable::Visitor* visitor, 
		bool debugRenderables)
	{
		if (mRenderer)
		{
			mRenderer->visitRenderables(visitor, debugRenderables);
		}
	}
	//---------------------------------------------------------------------
    void ParticleSystemEx::initParameters(void)
    {
        if (createParamDictionary("ParticleSystemEx"))
        {
            ParamDictionary* dict = getParamDictionary();

            dict->addParameter(ParameterDef("quota", 
                "The maximum number of particle allowed at once in this system.",
                PT_UNSIGNED_INT),
                &msQuotaCmd);

            dict->addParameter(ParameterDef("emit_emitter_quota", 
                "The maximum number of emitters to be emitted at once in this system.",
                PT_UNSIGNED_INT),
				&msEmittedEmitterQuotaCmd);

			dict->addParameter(ParameterDef("material", 
                "The name of the material to be used to render all particles in this system.",
                PT_STRING),
                &msMaterialCmd);

            dict->addParameter(ParameterDef("particle_width", 
                "The width of particles in world units.",
                PT_REAL),
                &msWidthCmd);

            dict->addParameter(ParameterDef("particle_height", 
                "The height of particles in world units.",
                PT_REAL),
                &msHeightCmd);

            dict->addParameter(ParameterDef("cull_each", 
                "If true, each particle is culled in it's own right. If false, the entire system is culled as a whole.",
                PT_BOOL),
                &msCullCmd);

			dict->addParameter(ParameterDef("renderer", 
				"Sets the particle system renderer to use (default 'billboard').",
				PT_STRING),
				&msRendererCmd);

			dict->addParameter(ParameterDef("sorted", 
				"Sets whether particles should be sorted relative to the camera. ",
				PT_BOOL),
				&msSortedCmd);

			dict->addParameter(ParameterDef("local_space", 
				"Sets whether particles should be kept in local space rather than "
				"emitted into world space. ",
				PT_BOOL),
				&msLocalSpaceCmd);

			dict->addParameter(ParameterDef("iteration_interval", 
				"Sets a fixed update interval for the system, or 0 for the frame rate. ",
				PT_REAL),
				&msIterationIntervalCmd);

			dict->addParameter(ParameterDef("nonvisible_update_timeout", 
				"Sets a timeout on updates to the system if the system is not visible "
				"for the given number of seconds (0 to always update)",
				PT_REAL),
				&msNonvisibleTimeoutCmd);

        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_updateBounds()
    {

        if (mParentNode && (mBoundsAutoUpdate || mBoundsUpdateTime > 0.0f))
        {
            if (mActiveParticles.empty())
            {
                // No particles, reset to null if auto update bounds
                if (mBoundsAutoUpdate)
                {
                    mWorldAABB.setNull();
                }
            }
            else
            {
                Vector3 min;
                Vector3 max;
                if (!mBoundsAutoUpdate && mWorldAABB.isFinite())
                {
                    // We're on a limit, grow rather than reset each time
                    // so that we pick up the worst case scenario
                    min = mWorldAABB.getMinimum();
                    max = mWorldAABB.getMaximum();
                }
                else
                {
                    min.x = min.y = min.z = Math::POS_INFINITY;
                    max.x = max.y = max.z = Math::NEG_INFINITY;
                }
                ActiveParticleList::iterator p;
                Vector3 halfScale = Vector3::UNIT_SCALE * 0.5;
                Vector3 defaultPadding = 
                    halfScale * std::max(mDefaultHeight, mDefaultWidth);
                for (p = mActiveParticles.begin(); p != mActiveParticles.end(); ++p)
                {
                    if ((*p)->mOwnDimensions)
                    {
                        Vector3 padding = 
                            halfScale * std::max((*p)->mWidth, (*p)->mHeight);
                        min.makeFloor((*p)->position - padding);
                        max.makeCeil((*p)->position + padding);
                    }
                    else
                    {
                        min.makeFloor((*p)->position - defaultPadding);
                        max.makeCeil((*p)->position + defaultPadding);
                    }
                }
                mWorldAABB.setExtents(min, max);
            }


            if (mLocalSpace)
            {
                // Merge calculated box with current AABB to preserve any user-set AABB
                mAABB.merge(mWorldAABB);
            }
            else
            {
                // We've already put particles in world space to decouple them from the
                // node transform, so reverse transform back since we're expected to 
                // provide a local AABB
                AxisAlignedBox newAABB(mWorldAABB);
                newAABB.transformAffine(mParentNode->_getFullTransform().inverseAffine());

                // Merge calculated box with current AABB to preserve any user-set AABB
                mAABB.merge(newAABB);
            }

            mParentNode->needUpdate();
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::fastForward(Real time, Real interval)
    {
        // First make sure all transforms are up to date

        for (Real ftime = 0; ftime < time; ftime += interval)
        {
            _update(interval);
        }
    }
	//-----------------------------------------------------------------------
	void ParticleSystemEx::setEmitting(bool v)
	{
		mIsEmitting = v;
	}
	//-----------------------------------------------------------------------
	bool ParticleSystemEx::getEmitting() const
	{
		return mIsEmitting;
	}
    //-----------------------------------------------------------------------
    const String& ParticleSystemEx::getMovableType(void) const
    {
        return ParticleSystemFactoryEx::FACTORY_TYPE_NAME;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_notifyParticleResized(void)
    {
        if (mRenderer)
        {
            mRenderer->_notifyParticleResized();
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_notifyParticleRotated(void)
    {
        if (mRenderer)
        {
            mRenderer->_notifyParticleRotated();
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setDefaultDimensions( Real width, Real height )
    {
        mDefaultWidth = width;
        mDefaultHeight = height;
        if (mRenderer)
        {
            mRenderer->_notifyDefaultDimensions(width, height);
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setDefaultWidth(Real width)
    {
        mDefaultWidth = width;
        if (mRenderer)
        {
            mRenderer->_notifyDefaultDimensions(mDefaultWidth, mDefaultHeight);
        }
    }
    //-----------------------------------------------------------------------
    Real ParticleSystemEx::getDefaultWidth(void) const
    {
        return mDefaultWidth;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setDefaultHeight(Real height)
    {
        mDefaultHeight = height;
        if (mRenderer)
        {
            mRenderer->_notifyDefaultDimensions(mDefaultWidth, mDefaultHeight);
        }
    }
    //-----------------------------------------------------------------------
    Real ParticleSystemEx::getDefaultHeight(void) const
    {
        return mDefaultHeight;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_notifyCurrentCamera(Camera* cam)
    {
		MovableObject::_notifyCurrentCamera(cam);

		// Record visible
		if (isVisible())
		{			
			mLastVisibleFrame = Root::getSingleton().getNextFrameNumber();
			mTimeSinceLastVisible = 0.0f;

			if (mSorted)
			{
				_sortParticles(cam);
			}

			if (mRenderer)
			{
				if (!mIsRendererConfigured)
					configureRenderer();

				mRenderer->_notifyCurrentCamera(cam);
			}
		}
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_notifyAttached(Node* parent, bool isTagPoint)
    {
        MovableObject::_notifyAttached(parent, isTagPoint);
        if (mRenderer && mIsRendererConfigured)
        {
            mRenderer->_notifyAttached(parent, isTagPoint);
        }

        if (parent && !mTimeController)
        {
            // Assume visible
            mTimeSinceLastVisible = 0;
            mLastVisibleFrame = Root::getSingleton().getNextFrameNumber();

            // Create time controller when attached
            ControllerManager& mgr = ControllerManager::getSingleton(); 
            ControllerValueRealPtr updValue(OGRE_NEW ParticleSystemUpdateValue(this));
            mTimeController = mgr.createFrameTimePassthroughController(updValue);
        }
        else if (!parent && mTimeController)
        {
            // Destroy controller
            ControllerManager::getSingleton().destroyController(mTimeController);
            mTimeController = 0;
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setMaterialName( const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */)
    {
        mMaterialName = name;
        if (mIsRendererConfigured)
        {
            MaterialPtr mat = MaterialManager::getSingleton().load(
                mMaterialName, mResourceGroupName);
            mRenderer->_setMaterial(mat);
        }
    }
    //-----------------------------------------------------------------------
    const String& ParticleSystemEx::getMaterialName(void) const
    {
        return mMaterialName;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::clear()
    {
        // Notify renderer if exists
        if (mRenderer)
        {
            mRenderer->_notifyParticleCleared(mActiveParticles);
        }

        // Move actives to free list
        mFreeParticles.splice(mFreeParticles.end(), mActiveParticles);

        // Add active emitted emitters to free list
		addActiveEmittedEmittersToFreeList();

		// Remove all active emitted emitter instances
		mActiveEmittedEmitters.clear();

		// Reset update remain time
        mUpdateRemainTime = 0;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setRenderer(const String& rendererName)
    {
		if (mRenderer)
		{
			// Destroy existing
			destroyVisualParticles(0, mParticlePool.size());
			ParticleSystemManagerEx::getSingleton()._destroyRenderer(mRenderer);
			mRenderer = 0;
		}

        if (!rendererName.empty())
        {
			mRenderer = ParticleSystemManagerEx::getSingleton()._createRenderer(rendererName);
            mIsRendererConfigured = false;
        }
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::configureRenderer(void)
    {
        // Actual allocate particles
        size_t currSize = mParticlePool.size();
        size_t size = mPoolSize;
        if( currSize < size )
        {
            this->increasePool(size);

            for( size_t i = currSize; i < size; ++i )
            {
                // Add new items to the queue
                mFreeParticles.push_back( mParticlePool[i] );
            }

            // Tell the renderer, if already configured
            if (mRenderer && mIsRendererConfigured)
            {
                mRenderer->_notifyParticleQuota(size);
            }
        }

        if (mRenderer && !mIsRendererConfigured)
        {
            mRenderer->_notifyParticleQuota(mParticlePool.size());
            mRenderer->_notifyAttached(mParentNode, mParentIsTagPoint);
            mRenderer->_notifyDefaultDimensions(mDefaultWidth, mDefaultHeight);
            createVisualParticles(0, mParticlePool.size());
            MaterialPtr mat = MaterialManager::getSingleton().load(
                mMaterialName, mResourceGroupName);
            mRenderer->_setMaterial(mat);
			if (mRenderQueueIDSet)
				mRenderer->setRenderQueueGroup(mRenderQueueID);
			mRenderer->setKeepParticlesInLocalSpace(mLocalSpace);
            mIsRendererConfigured = true;
        }
    }
    //-----------------------------------------------------------------------
    ParticleSystemRendererEx* ParticleSystemEx::getRenderer(void) const
    {
        return mRenderer;
    }
    //-----------------------------------------------------------------------
    const String& ParticleSystemEx::getRendererName(void) const
    {
        if (mRenderer)
        {
            return mRenderer->getType();
        }
        else
        {
            return StringUtil::BLANK;
        }
    }
    //-----------------------------------------------------------------------
    bool ParticleSystemEx::getCullIndividually(void) const
    {
        return mCullIndividual;
    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setCullIndividually(bool cullIndividual)
    {
        mCullIndividual = cullIndividual;
    }
    //-----------------------------------------------------------------------
	void ParticleSystemEx::createVisualParticles(size_t poolstart, size_t poolend)
	{
		ParticlePool::iterator i = mParticlePool.begin();
		ParticlePool::iterator iend = mParticlePool.begin();
		std::advance(i, poolstart);
		std::advance(iend, poolend);
		for (; i != iend; ++i)
		{
			(*i)->_notifyVisualData(
				mRenderer->_createVisualData());
		}
	}
    //-----------------------------------------------------------------------
	void ParticleSystemEx::destroyVisualParticles(size_t poolstart, size_t poolend)
	{
		ParticlePool::iterator i = mParticlePool.begin();
		ParticlePool::iterator iend = mParticlePool.begin();
		std::advance(i, poolstart);
		std::advance(iend, poolend);
		for (; i != iend; ++i)
		{
			mRenderer->_destroyVisualData((*i)->getVisualData());
			(*i)->_notifyVisualData(0);
		}
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setBounds(const AxisAlignedBox& aabb)
    {
        mAABB = aabb;
        mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);

    }
    //-----------------------------------------------------------------------
    void ParticleSystemEx::setBoundsAutoUpdated(bool autoUpdate, Real stopIn)
    {
        mBoundsAutoUpdate = autoUpdate;
        mBoundsUpdateTime = stopIn;
    }
	//-----------------------------------------------------------------------
	void ParticleSystemEx::setRenderQueueGroup(uint8 queueID)
	{
		MovableObject::setRenderQueueGroup(queueID);
		if (mRenderer)
		{
			mRenderer->setRenderQueueGroup(queueID);
		}
	}
	//-----------------------------------------------------------------------
	void ParticleSystemEx::setKeepParticlesInLocalSpace(bool keepLocal)
	{
		mLocalSpace = keepLocal;
		if (mRenderer)
		{
			mRenderer->setKeepParticlesInLocalSpace(keepLocal);
		}
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::_sortParticles(Camera* cam)
    {
        if (mRenderer)
        {
            SortMode sortMode = mRenderer->_getSortMode();
            if (sortMode == SM_DIRECTION)
            {
                Vector3 camDir = cam->getDerivedDirection();
                if (mLocalSpace)
                {
                    // transform the camera direction into local space
                    camDir = mParentNode->_getDerivedOrientation().UnitInverse() * camDir;
                }
                mRadixSorter.sort(mActiveParticles, SortByDirectionFunctor(- camDir));
            }
            else if (sortMode == SM_DISTANCE)
            {
                Vector3 camPos = cam->getDerivedPosition();
                if (mLocalSpace)
                {
                    // transform the camera position into local space
                    camPos = mParentNode->_getDerivedOrientation().UnitInverse() *
                        (camPos - mParentNode->_getDerivedPosition()) / mParentNode->_getDerivedScale();
                }
                mRadixSorter.sort(mActiveParticles, SortByDistanceFunctor(camPos));
            }
        }
    }
    ParticleSystemEx::SortByDirectionFunctor::SortByDirectionFunctor(const Vector3& dir)
        : sortDir(dir)
    {
    }
    float ParticleSystemEx::SortByDirectionFunctor::operator()(ParticleEx* p) const
    {
        return sortDir.dotProduct(p->position);
    }
    ParticleSystemEx::SortByDistanceFunctor::SortByDistanceFunctor(const Vector3& pos)
        : sortPos(pos)
    {
    }
    float ParticleSystemEx::SortByDistanceFunctor::operator()(ParticleEx* p) const
    {
        // Sort descending by squared distance
        return - (sortPos - p->position).squaredLength();
    }
	//-----------------------------------------------------------------------
	uint32 ParticleSystemEx::getTypeFlags(void) const
	{
		return SceneManager::FX_TYPE_MASK;
	}
	//-----------------------------------------------------------------------
    void ParticleSystemEx::initialiseEmittedEmitters(void)
    {
		// Initialise the pool if needed
		size_t currSize = 0;
		if (mEmittedEmitterPool.empty())
		{
			if (mEmittedEmitterPoolInitialised)
			{
				// It was already initialised, but apparently no emitted emitters were used
				return;
			}
			else
			{
				initialiseEmittedEmitterPool();
			}
		}
		else
		{
			EmittedEmitterPool::iterator i;
			for (i = mEmittedEmitterPool.begin(); i != mEmittedEmitterPool.end(); ++i)
			{
				currSize += i->second.size();
			}
		}

        size_t size = mEmittedEmitterPoolSize;
        if( currSize < size && !mEmittedEmitterPool.empty())
        {
			// Increase the pool. Equally distribute over all vectors in the map
            increaseEmittedEmitterPool(size);
			
			// Add new items to the free list
			addFreeEmittedEmitters();
		}
    }

	//-----------------------------------------------------------------------
	void ParticleSystemEx::initialiseEmittedEmitterPool(void)
	{
		if (mEmittedEmitterPoolInitialised)
			return;

		// Run through mEmitters and add keys to the pool
		ParticleEmitterList::iterator emitterIterator;
		ParticleEmitterList::iterator emitterIteratorInner;
		ParticleEmitterEx* emitter = 0;
		ParticleEmitterEx* emitterInner = 0;
		for (emitterIterator = mEmitters.begin(); emitterIterator != mEmitters.end(); ++emitterIterator)
		{
			// Determine the names of all emitters that are emitted
			emitter = *emitterIterator ;
			if (emitter && emitter->getEmittedEmitter() != StringUtil::BLANK)
			{
				// This one will be emitted, register its name and leave the vector empty!
				EmittedEmitterList empty;
				mEmittedEmitterPool.insert(make_pair(emitter->getEmittedEmitter(), empty));
			}

			// Determine whether the emitter itself will be emitted and set the 'mEmitted' attribute
			for (emitterIteratorInner = mEmitters.begin(); emitterIteratorInner != mEmitters.end(); ++emitterIteratorInner)
			{
				emitterInner = *emitterIteratorInner;
				if (emitter && 
					emitterInner && 
					emitter->getName() != StringUtil::BLANK && 
					emitter->getName() == emitterInner->getEmittedEmitter())
				{
					emitter->setEmitted(true);
					break;
				}
				else
				{
					// Set explicitly to 'false' although the default value is already 'false'
					emitter->setEmitted(false);
				}
			}
		}

		mEmittedEmitterPoolInitialised = true;
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::increaseEmittedEmitterPool(size_t size)
    {
		// Don't proceed if the pool doesn't contain any keys of emitted emitters
		if (mEmittedEmitterPool.empty())
			return;

		EmittedEmitterPool::iterator emittedEmitterPoolIterator;
		ParticleEmitterList::iterator emitterIterator;
		ParticleEmitterEx* emitter = 0;
		ParticleEmitterEx* clonedEmitter = 0;
		String name = StringUtil::BLANK;
		EmittedEmitterList* e = 0;
		size_t maxNumberOfEmitters = size / mEmittedEmitterPool.size(); // equally distribute the number for each emitted emitter list
		size_t oldSize = 0;
	
		// Run through mEmittedEmitterPool and search for every key (=name) its corresponding emitter in mEmitters
		for (emittedEmitterPoolIterator = mEmittedEmitterPool.begin(); emittedEmitterPoolIterator != mEmittedEmitterPool.end(); ++emittedEmitterPoolIterator)
        {
			name = emittedEmitterPoolIterator->first;
			e = &emittedEmitterPoolIterator->second;

			// Search the correct emitter in the mEmitters vector
			emitter = 0;
			for (emitterIterator = mEmitters.begin(); emitterIterator != mEmitters.end(); ++emitterIterator)
			{
				emitter = *emitterIterator;
				if (emitter && 
					name != StringUtil::BLANK && 
					name == emitter->getName())
				{		
					// Found the right emitter, clone each emitter a number of times
					oldSize = e->size();
					for (size_t t = oldSize; t < maxNumberOfEmitters; ++t)
					{
						clonedEmitter = ParticleSystemManagerEx::getSingleton()._createEmitter(emitter->getType(), this);
						emitter->copyParametersTo(clonedEmitter);
						clonedEmitter->setEmitted(emitter->isEmitted()); // is always 'true' by the way, but just in case

						// Initially deactivate the emitted emitter if duration/repeat_delay are set
						if (clonedEmitter->getDuration() > 0.0f && 
							(clonedEmitter->getRepeatDelay() > 0.0f || clonedEmitter->getMinRepeatDelay() > 0.0f || clonedEmitter->getMinRepeatDelay() > 0.0f))
							clonedEmitter->setEnabled(false);

						// Add cloned emitters to the pool
						e->push_back(clonedEmitter);
					}
				}
			}
        }
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::addFreeEmittedEmitters(void)
    {
		// Don't proceed if the EmittedEmitterPool is empty
		if (mEmittedEmitterPool.empty())
			return;

		// Copy all pooled emitters to the free list
		EmittedEmitterPool::iterator emittedEmitterPoolIterator;
		EmittedEmitterList::iterator emittedEmitterIterator;
		EmittedEmitterList* emittedEmitters = 0;
		list<ParticleEmitterEx*>::type* fee = 0;
		String name = StringUtil::BLANK;

		// Run through the emittedEmitterPool map
		for (emittedEmitterPoolIterator = mEmittedEmitterPool.begin(); emittedEmitterPoolIterator != mEmittedEmitterPool.end(); ++emittedEmitterPoolIterator)
        {
			name = emittedEmitterPoolIterator->first;
			emittedEmitters = &emittedEmitterPoolIterator->second;
			fee = findFreeEmittedEmitter(name);

			// If its not in the map, create an empty one
			if (!fee)
			{
				FreeEmittedEmitterList empty;
				mFreeEmittedEmitters.insert(make_pair(name, empty));
				fee = findFreeEmittedEmitter(name);
			}

			// Check anyway if its ok now
			if (!fee)
				return; // forget it!

			// Add all emitted emitters from the pool to the free list
			for(emittedEmitterIterator = emittedEmitters->begin(); emittedEmitterIterator != emittedEmitters->end(); ++emittedEmitterIterator)
			{
				fee->push_back(*emittedEmitterIterator);
			}
		}
	}
    //-----------------------------------------------------------------------
    void ParticleSystemEx::removeAllEmittedEmitters(void)
    {
		EmittedEmitterPool::iterator emittedEmitterPoolIterator;
		EmittedEmitterList::iterator emittedEmitterListIterator;
		EmittedEmitterList* e = 0;
        for (emittedEmitterPoolIterator = mEmittedEmitterPool.begin(); emittedEmitterPoolIterator != mEmittedEmitterPool.end(); ++emittedEmitterPoolIterator)
        {
			e = &emittedEmitterPoolIterator->second;
			for (emittedEmitterListIterator = e->begin(); emittedEmitterListIterator != e->end(); ++emittedEmitterListIterator)
			{
				ParticleSystemManagerEx::getSingleton()._destroyEmitter(*emittedEmitterListIterator);
			}
			e->clear();
        }

		// Dont leave any references behind
		mEmittedEmitterPool.clear();
		mFreeEmittedEmitters.clear();
		mActiveEmittedEmitters.clear();
    }
	//-----------------------------------------------------------------------
	list<ParticleEmitterEx*>::type* ParticleSystemEx::findFreeEmittedEmitter (const String& name)
	{
		FreeEmittedEmitterMap::iterator it;
		it = mFreeEmittedEmitters.find (name);
		if (it != mFreeEmittedEmitters.end())
		{
			// Found it
			return &it->second;
		}

		return 0;
	}
	//-----------------------------------------------------------------------
	void ParticleSystemEx::removeFromActiveEmittedEmitters (ParticleEmitterEx* emitter)
	{
		assert(emitter && "Emitter to be removed is 0!");
		ActiveEmittedEmitterList::iterator itActiveEmit;
		for (itActiveEmit = mActiveEmittedEmitters.begin(); itActiveEmit != mActiveEmittedEmitters.end(); ++itActiveEmit)
		{
			if (emitter == (*itActiveEmit))
			{
				mActiveEmittedEmitters.erase(itActiveEmit);
				break;
			}
		}
	}
	//-----------------------------------------------------------------------
	void ParticleSystemEx::addActiveEmittedEmittersToFreeList (void)
	{
		ActiveEmittedEmitterList::iterator itActiveEmit;
		for (itActiveEmit = mActiveEmittedEmitters.begin(); itActiveEmit != mActiveEmittedEmitters.end(); ++itActiveEmit)
		{
			list<ParticleEmitterEx*>::type* fee = findFreeEmittedEmitter ((*itActiveEmit)->getName());
			if (fee)
				fee->push_back(*itActiveEmit);
		}
	}
	//-----------------------------------------------------------------------
	void ParticleSystemEx::_notifyReorganiseEmittedEmitterData (void)
	{
		removeAllEmittedEmitters();
		mEmittedEmitterPoolInitialised = false; // Dont rearrange immediately; it will be performed in the regular flow
	}
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdCull::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const ParticleSystemEx*>(target)->getCullIndividually() );
    }
    void ParticleSystemEx::CmdCull::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setCullIndividually(
            StringConverter::parseBool(val));
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdHeight::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const ParticleSystemEx*>(target)->getDefaultHeight() );
    }
    void ParticleSystemEx::CmdHeight::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setDefaultHeight(
            StringConverter::parseReal(val));
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdWidth::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const ParticleSystemEx*>(target)->getDefaultWidth() );
    }
    void ParticleSystemEx::CmdWidth::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setDefaultWidth(
            StringConverter::parseReal(val));
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdMaterial::doGet(const void* target) const
    {
        return static_cast<const ParticleSystemEx*>(target)->getMaterialName();
    }
    void ParticleSystemEx::CmdMaterial::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setMaterialName(val);
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdQuota::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const ParticleSystemEx*>(target)->getParticleQuota() );
    }
    void ParticleSystemEx::CmdQuota::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setParticleQuota(
            StringConverter::parseUnsignedInt(val));
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdEmittedEmitterQuota::doGet(const void* target) const
    {
        return StringConverter::toString(
            static_cast<const ParticleSystemEx*>(target)->getEmittedEmitterQuota() );
    }
    void ParticleSystemEx::CmdEmittedEmitterQuota::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setEmittedEmitterQuota(
            StringConverter::parseUnsignedInt(val));
    }
    //-----------------------------------------------------------------------
    String ParticleSystemEx::CmdRenderer::doGet(const void* target) const
    {
        return static_cast<const ParticleSystemEx*>(target)->getRendererName();
    }
    void ParticleSystemEx::CmdRenderer::doSet(void* target, const String& val)
    {
        static_cast<ParticleSystemEx*>(target)->setRenderer(val);
    }
	//-----------------------------------------------------------------------
	String ParticleSystemEx::CmdSorted::doGet(const void* target) const
	{
		return StringConverter::toString(
			static_cast<const ParticleSystemEx*>(target)->getSortingEnabled());
	}
	void ParticleSystemEx::CmdSorted::doSet(void* target, const String& val)
	{
		static_cast<ParticleSystemEx*>(target)->setSortingEnabled(
			StringConverter::parseBool(val));
	}
	//-----------------------------------------------------------------------
	String ParticleSystemEx::CmdLocalSpace::doGet(const void* target) const
	{
		return StringConverter::toString(
			static_cast<const ParticleSystemEx*>(target)->getKeepParticlesInLocalSpace());
	}
	void ParticleSystemEx::CmdLocalSpace::doSet(void* target, const String& val)
	{
		static_cast<ParticleSystemEx*>(target)->setKeepParticlesInLocalSpace(
			StringConverter::parseBool(val));
	}
	//-----------------------------------------------------------------------
	String ParticleSystemEx::CmdIterationInterval::doGet(const void* target) const
	{
		return StringConverter::toString(
			static_cast<const ParticleSystemEx*>(target)->getIterationInterval());
	}
	void ParticleSystemEx::CmdIterationInterval::doSet(void* target, const String& val)
	{
		static_cast<ParticleSystemEx*>(target)->setIterationInterval(
			StringConverter::parseReal(val));
	}
	//-----------------------------------------------------------------------
	String ParticleSystemEx::CmdNonvisibleTimeout::doGet(const void* target) const
	{
		return StringConverter::toString(
			static_cast<const ParticleSystemEx*>(target)->getNonVisibleUpdateTimeout());
	}
	void ParticleSystemEx::CmdNonvisibleTimeout::doSet(void* target, const String& val)
	{
		static_cast<ParticleSystemEx*>(target)->setNonVisibleUpdateTimeout(
			StringConverter::parseReal(val));
	}
   //-----------------------------------------------------------------------
    ParticleAffectorEx::~ParticleAffectorEx() 
    {
    }
    //-----------------------------------------------------------------------
    ParticleAffectorFactoryEx::~ParticleAffectorFactoryEx() 
    {
        // Destroy all affectors
        vector<ParticleAffectorEx*>::type::iterator i;
        for (i = mAffectors.begin(); i != mAffectors.end(); ++i)
        {
            OGRE_DELETE (*i);
        }
            
        mAffectors.clear();

    }
    //-----------------------------------------------------------------------
    void ParticleAffectorFactoryEx::destroyAffector(ParticleAffectorEx* e)
    {
        vector<ParticleAffectorEx*>::type::iterator i;
        for (i = mAffectors.begin(); i != mAffectors.end(); ++i)
        {
            if ((*i) == e)
            {
                mAffectors.erase(i);
                OGRE_DELETE e;
                break;
            }
        }
    }

}
